// SelectObjectDlg.cpp : implementation file
//

#include "StdAfx.h"
#include "SelectObjectDlg.h"

#include "Objects\Group.h"
#include "Objects\EntityObject.h"
#include "Objects\BrushObject.h"
#include "DisplaySettings.h"
#include "ViewManager.h"
#include "Material\Material.h"
#include "LinkTool.h"
#include "HyperGraph/FlowGraphHelpers.h"
#include "HyperGraph/FlowGraphManager.h"
#include "HyperGraph/FlowGraph.h"
#include "HyperGraph/FlowGraphNode.h"

#ifndef MAXUINT
#define MAXUINT ((UINT)~((UINT)0))
#endif

#ifndef MAXINT
#define MAXINT ((INT)(MAXUINT >> 1))
#endif

#define CHILD_INDENT 1

#define DISPLAY_VISIBLE 0
#define DISPLAY_HIDDEN 1
#define DISPLAY_FROZEN 2

#define COLUMN_NAME 0
#define COLUMN_SELECTED 1
#define COLUMN_TYPE 2
#define COLUMN_LAYER 3
#define COLUMN_DEFAULTMATERIAL 4
#define COLUMN_CUSTOMMATERIAL 5
#define COLUMN_SMART_OBJECT 6
#define COLUMN_TRACKVIEW 7
#define COLUMN_FLOWGRAPH 8
#define COLUMN_GEOMETRY 9
#define COLUMN_GEOMETRYCOUNT 10
#define COLUMN_GEOMETRYLOD 11
#define COLUMN_MINSPEC 12

#define LIST_BITMAP_ANY 0
#define LIST_BITMAP_ENTITY 1
#define LIST_BITMAP_BRUSH 2
#define LIST_BITMAP_TAGPOINT 3
#define LIST_BITMAP_PATH 4
#define LIST_BITMAP_VOLUME 5
#define LIST_BITMAP_GROUP 7

#define IDT_TIMER_0	WM_USER + 200 
#define IDT_TIMER_1	IDT_TIMER_0 + 1
#define FILE_FILTER_DELAY_OBJ_COUNT 18000
#define FILE_FILTER_DELAY 650

#define UNIX_SLASH "/"
#define WIN_SLASH "\\"
#define CLOSED_GRP_STR " (Closed)]"

CSelectObjectDlg::ObjToStrMap CSelectObjectDlg::s_flowGraphMap;
CSelectObjectDlg::ObjToStrMap CSelectObjectDlg::s_trackViewMap;
CSelectObjectDlg::TGeometryCountMap CSelectObjectDlg::m_mGeomCountMap;
CSelectObjectDlg::TSpecMap CSelectObjectDlg::m_specMap;

int CSelectObjectDlg::m_sortFlags = 0;
int CSelectObjectDlg::m_displayMode = DISPLAY_VISIBLE;
int CSelectObjectDlg::m_nLastSelectedItemPosition = 0;

BOOL	CSelectObjectDlg::m_bEntity = TRUE;
BOOL	CSelectObjectDlg::m_bPrefabs = TRUE;
BOOL	CSelectObjectDlg::m_bOther = TRUE;
BOOL	CSelectObjectDlg::m_bAIPoint = TRUE;
BOOL	CSelectObjectDlg::m_bTagPoint = TRUE;
BOOL	CSelectObjectDlg::m_bGroups = TRUE;
BOOL	CSelectObjectDlg::m_bVolumes = TRUE;
BOOL	CSelectObjectDlg::m_bShapes = TRUE;
BOOL  CSelectObjectDlg::m_bBrushes = TRUE;
BOOL  CSelectObjectDlg::m_bSolids = TRUE;

BOOL  CSelectObjectDlg::m_bAutoselect = FALSE;
BOOL  CSelectObjectDlg::m_bTree = FALSE;
BOOL  CSelectObjectDlg::m_bSearchInsideObjects = false;

static const CString g_emptyString = "";

//////////////////////////////////////////////////////////////////////////
static const CString& GetObjectName( CBaseObject *pObject )
{
	static CString resultBuffer = "";

	if (!pObject)
		return resultBuffer;

	CGroup *pObjGroup = pObject->GetGroup();

	if (pObjGroup)
	{
		resultBuffer = "[";
		resultBuffer += pObjGroup->GetName();

		if (pObjGroup->IsOpen())
			resultBuffer += "]";
		else
			resultBuffer += CLOSED_GRP_STR;

		resultBuffer += pObject->GetName();
		return resultBuffer;
	}
	else if ( (pObject->GetType() == OBJTYPE_GROUP) || (pObject->GetType() == OBJTYPE_PREFAB) )
	{
		resultBuffer = "[";
		resultBuffer += pObject->GetName();

		CGroup *pGroupObj = (CGroup*)pObject;

		if (pGroupObj)
		{
			if (pGroupObj->IsOpen())
				resultBuffer += "]";
			else
				resultBuffer += CLOSED_GRP_STR;
		}

		return resultBuffer;
	}
	else
		return pObject->GetName();
}

//////////////////////////////////////////////////////////////////////////
static const CString& GetMaterialName( CBaseObject* pObject, bool bGetCustomMaterial = FALSE )
{
	static CString sEffect("");
	static CString custMaterialStr("");
	static CString defMaterialStr("");

	sEffect=="";
	custMaterialStr="";
	defMaterialStr="";

	// Get default material stored in CGF file
	if (pObject->IsKindOf(RUNTIME_CLASS(CBrushObject)))
	{
		CBrushObject *pBrush = (CBrushObject *)pObject;

		if (!pBrush)
			return g_emptyString;

		if (!pBrush->GetIStatObj())
			return g_emptyString;

		if (!pBrush->GetIStatObj()->GetMaterial())
			return g_emptyString;

		defMaterialStr = pBrush->GetIStatObj()->GetMaterial()->GetName();
		defMaterialStr = defMaterialStr.MakeLower();
		defMaterialStr.Replace(UNIX_SLASH,WIN_SLASH);
	}
		
	// Get material assigned by the user
	CMaterial *pMtl = pObject->GetMaterial();
	if (pMtl)
	{
		custMaterialStr=pMtl->GetName();
		custMaterialStr=custMaterialStr.MakeLower();
		custMaterialStr.Replace(UNIX_SLASH,WIN_SLASH);
	}

	if (!defMaterialStr.IsEmpty() || !custMaterialStr.IsEmpty())
	{
		if (bGetCustomMaterial)
		{
			if (!custMaterialStr.IsEmpty() && custMaterialStr!=defMaterialStr)
				return custMaterialStr;
			else
				return g_emptyString;
		}
		else
		{
			return defMaterialStr;
		}
	}

	// Entity
	if (pObject->IsKindOf(RUNTIME_CLASS(CEntityObject)))
	{
		CEntityObject *pEntity = (CEntityObject*)pObject;
		if (pEntity->GetProperties())
		{
			IVariable *pVar = pEntity->GetProperties()->FindVariable( "ParticleEffect" );
			if (pVar)
			{
				pVar->Get(sEffect);
				return sEffect;
			}		
		}
	}

	return g_emptyString;
}

//////////////////////////////////////////////////////////////////////////
static const CString& GetGeometryFile( CBaseObject* pObject )
{
	static CString resultBuffer = "";

	resultBuffer ="";

	if(pObject->IsKindOf(RUNTIME_CLASS(CBrushObject)))
	{
		CBrushObject * pObj = (CBrushObject*)pObject;
		resultBuffer = pObj->GetGeometryFile().MakeLower();
		resultBuffer.Replace(UNIX_SLASH,WIN_SLASH);
		return resultBuffer;
	}
	else if(pObject->IsKindOf(RUNTIME_CLASS(CGeomEntity)))
	{
		CGeomEntity * pObj = (CGeomEntity*)pObject;
		resultBuffer= pObj->GetGeometryFile().MakeLower();
		resultBuffer.Replace(UNIX_SLASH,WIN_SLASH);
		return resultBuffer;
	}
	else if(pObject->IsKindOf(RUNTIME_CLASS(CEntityObject)))
	{
		CEntityObject *pEntity = static_cast<CEntityObject*>(pObject);
		IRenderNode* pEngineNode = pEntity->GetEngineNode();

		if (pEngineNode)
		{
			IStatObj *pEntityStatObj = pEngineNode->GetEntityStatObj();
			ICharacterInstance *iEntityCharacter = pEngineNode->GetEntityCharacter(0);

			if (pEntityStatObj)
				resultBuffer = pEntityStatObj->GetFilePath();

			if (resultBuffer.IsEmpty() && iEntityCharacter)
			{
				resultBuffer = iEntityCharacter->GetFilePath();
			}

			if ( resultBuffer.IsEmpty() && pEntity->GetProperties() )
			{
				IVariable *pVar = pEntity->GetProperties()->FindVariable( "Model" );
				if (pVar)
				{
					pVar->Get(resultBuffer);
				}		
			}

			if (!resultBuffer.IsEmpty())
			{
				resultBuffer.MakeLower();
				resultBuffer.Replace(UNIX_SLASH,WIN_SLASH);
			}

			return resultBuffer;
		}
	}

	return g_emptyString;
}


//////////////////////////////////////////////////////////////////////////
static const CString& GetSmartObject( CBaseObject* pObject )
{
	static CString soClass = "";
	if (pObject->IsKindOf(RUNTIME_CLASS(CEntityObject)))
	{
		CEntityObject *pEntity = (CEntityObject*)pObject;
		soClass = pEntity->GetSmartObjectClass();
		return soClass;
	}

	return g_emptyString;
}

static int g_nCurSel = -1;

//////////////////////////////////////////////////////////////////////////
static int GetAIGroupID(CBaseObject * obj)
{
	if (obj->IsKindOf( RUNTIME_CLASS(CEntityObject) ))
	{
		CEntityObject * pEnt = (CEntityObject *)obj;
		CVarBlock * properties = pEnt->GetProperties2();
		if(properties)
		{
			IVariable * varEn = properties->FindVariable("groupid");
			if(varEn)
			{
				int groupid;
				varEn->Get(groupid);
				return groupid;
			}
		}
	}
	return -1;
}

//////////////////////////////////////////////////////////////////////////
const CString& CSelectObjectDlg::GetFlowGraphNames( CBaseObject* pObject )
{
	CSelectObjectDlg::ObjToStrMap::const_iterator itr 
		= CSelectObjectDlg::s_flowGraphMap.find(pObject);	
	if (itr == CSelectObjectDlg::s_flowGraphMap.end())
		return g_emptyString;
	else
		return itr->second;
}


//////////////////////////////////////////////////////////////////////////
const CString& CSelectObjectDlg::GetTrackViewName( CBaseObject* pObject )
{
	CSelectObjectDlg::ObjToStrMap::const_iterator itr 
		= CSelectObjectDlg::s_trackViewMap.find(pObject);	
	if (itr == CSelectObjectDlg::s_trackViewMap.end())
		return g_emptyString;
	else
		return itr->second;
}

//////////////////////////////////////////////////////////////////////////
static const CString& ComputeFlowGraphNames( CBaseObject* pObject )
{
	static CString result = "";
	if (pObject->IsKindOf(RUNTIME_CLASS(CEntityObject)))
	{
		CEntityObject *pEntity = static_cast<CEntityObject*>(pObject);
		std::vector<CFlowGraph*> flowgraphs;
		CFlowGraph *pEntityFG = 0;
		FlowGraphHelpers::FindGraphsForEntity(pEntity, flowgraphs, pEntityFG);
		CString name("");
		result = "";
		for (size_t i = 0; i < flowgraphs.size(); ++i)
		{
			if (i > 0)
				result += ",";
			FlowGraphHelpers::GetHumanName(flowgraphs[i], name);
			result += name;
			if (flowgraphs[i] == pEntityFG)
				result += "*";		// A special mark for an entity flow graph
		}
		return result;
	}

	return g_emptyString;
}

//////////////////////////////////////////////////////////////////////////
static const CString& ComputeTrackViewName( CBaseObject* pObject )
{
	static CString result = "";
	if (pObject->IsKindOf(RUNTIME_CLASS(CEntityObject)))
	{
		CEntityObject *pEntity = static_cast<CEntityObject*>(pObject);
		std::vector<IAnimNode*> nodeList;
		pEntity->GetAllRelatedAnimNodes(nodeList);
		result = "";
		for (size_t i = 0; i < nodeList.size(); ++i)
		{
			if (result.IsEmpty() == false)
				result += ",";
			IAnimSequence *pSequence = nodeList[i]->GetSequence();
			if (pSequence)
			{
				result += pSequence->GetFullName();
			}
		}
		return result;
	}

	return g_emptyString;
}

//////////////////////////////////////////////////////////////////////////
CSelectObjectDlg* CSelectObjectDlg::m_instance = 0;

class CSelectObjectViewPaneClass : public TRefCountBase<IViewPaneClass>
{
	//////////////////////////////////////////////////////////////////////////
	// IClassDesc
	//////////////////////////////////////////////////////////////////////////
	virtual ESystemClassID SystemClassID() { return ESYSTEM_CLASS_VIEWPANE; };
	virtual REFGUID ClassID()
	{
		// {E9D27BFE-6FC3-425a-9846-E1E52CEBB693}
		static const GUID guid = 
			{ 0xe9d27bfe, 0x6fc3, 0x425a, { 0x98, 0x46, 0xe1, 0xe5, 0x2c, 0xeb, 0xb6, 0x93 } };
		return guid;
	}
	virtual const char* ClassName() { return "Select Objects"; };
	virtual const char* Category() { return "Editor"; };
	//////////////////////////////////////////////////////////////////////////
	virtual CRuntimeClass* GetRuntimeClass() { return RUNTIME_CLASS(CSelectObjectDlg); };
	virtual const char* GetPaneTitle() { return _T("Select Objects"); };
	virtual EDockingDirection GetDockingDirection() { return DOCK_FLOAT; };
	virtual CRect GetPaneRect() { return CRect(0,0,500,690); };
	virtual CSize GetMinSize() { return CSize(500,690); }
	virtual bool SinglePane() { return true; };
	virtual bool WantIdleUpdate() { return true; };
};

IMPLEMENT_DYNCREATE(CSelectObjectDlg,CXTResizeDialog)

//////////////////////////////////////////////////////////////////////////
void CSelectObjectDlg::RegisterViewClass()
{
	GetIEditor()->GetClassFactory()->RegisterClass( new CSelectObjectViewPaneClass );
}


/////////////////////////////////////////////////////////////////////////////
// CSelectObjectDlg dialog

CSelectObjectDlg* CSelectObjectDlg::GetInstance()
{
	if (!m_instance)
	{
		m_instance = new CSelectObjectDlg;
	}
	return m_instance;
}

CSelectObjectDlg::CSelectObjectDlg(CWnd* pParent /*=NULL*/)
	: CXTResizeDialog(CSelectObjectDlg::IDD, pParent)
{
	m_bIgnoreObjectCallback = false;
	m_picking = false;
	m_instance = this;
	m_bIsLinkTool = false;
	m_bLayerModified = false;
	m_bTrackViewModified = false;
	m_bSearchInsideObjects = false;
	//SetResizeFlags( SZ_NOMINSIZE );
	SetFlag( xtResizeNoMinsize );

	Create( IDD,pParent );

	m_specMap[0]="All";
	m_specMap[1]="Low";
#if !defined(IS_CRYDEV)
	m_specMap[2]="Medium/Console";


#endif
	m_specMap[3]="High";
	m_specMap[4]="Very High";
	//m_specMap[7]="Detail";
}

void CSelectObjectDlg::DoDataExchange(CDataExchange* pDX)
{
	CXTResizeDialog::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_OBJECTS, m_list);
	DDX_Control(pDX, IDC_HIDE, m_hideBtn);
	DDX_Control(pDX, IDC_FREEZE, m_freezeBtn);
	DDX_Control(pDX, IDC_SEL_ALL, m_selAllBtn);
	DDX_Control(pDX, IDC_SEL_NONE, m_selNoneBtn);
	DDX_Control(pDX, IDC_SEL_INV, m_selInvBtn);
	DDX_Control(pDX, IDC_SELECT_PROPERTY_NAME, m_selByPropertyName);
	DDX_Control(pDX, IDC_SELECT_PROPERTY_VALUE, m_selByPropertyValue);
	DDX_Check(pDX, IDC_LIST_ENTITY, m_bEntity);
	DDX_Check(pDX, IDC_LIST_PREFABS, m_bPrefabs);
	DDX_Check(pDX, IDC_LIST_OTHER, m_bOther);
	DDX_Check(pDX, IDC_LIST_TAGPOINT, m_bTagPoint);
	DDX_Check(pDX, IDC_LIST_GROUPS, m_bGroups);
	DDX_Check(pDX, IDC_LIST_VOLUMES, m_bVolumes);
	DDX_Check(pDX, IDC_LIST_AIPOINTS, m_bAIPoint);
	DDX_Check(pDX, IDC_LIST_PATHS, m_bShapes);
	DDX_Check(pDX, IDC_LIST_BRUSHES, m_bBrushes);
	DDX_Check(pDX, IDC_LIST_SOLIDS, m_bSolids);
	DDX_Check(pDX, IDC_AUTOSELECT, m_bAutoselect);
	DDX_Check(pDX, IDC_TREE, m_bTree);
	DDX_Check(pDX, IDC_SEARCHALLOBJECTS, m_bSearchInsideObjects);
	DDX_Control( pDX,IDC_PROPERTY,m_propertyFilterCtrl );
	DDX_Control( pDX,IDC_NUM_OBJECTS,m_numObjects );
	DDX_Control(pDX,IDC_REFRESH,m_refreshBtn);
	DDX_Control(pDX,IDOK,m_selectBtn);
	DDX_Control(pDX,IDC_DELETE_SELECTED,m_deleteBtn);
	DDX_Control(pDX,IDC_COMBO_LAST_COLUMN,m_comboLastColumn);
}

BEGIN_MESSAGE_MAP(CSelectObjectDlg, CXTResizeDialog)
	ON_WM_DESTROY()
	ON_COMMAND(IDC_SEL_ALL, OnSelAll)
	ON_COMMAND(IDC_SEL_NONE, OnSelNone)
	ON_COMMAND(IDC_SEL_INV, OnSelInv)
	ON_NOTIFY(LVN_COLUMNCLICK, IDC_OBJECTS, OnColumnclickObjects)
	ON_EN_UPDATE(IDC_FASTFIND, OnUpdateFastfind)
	ON_NOTIFY(NM_CLICK, IDC_OBJECTS, OnClickObjects)
	ON_BN_CLICKED(IDC_VISIBLE, OnVisible)
	ON_BN_CLICKED(IDC_HIDDEN, OnHidden)
	ON_BN_CLICKED(IDC_FROZEN, OnFrozen)
	ON_BN_CLICKED(IDC_HIDE, OnHide)
	ON_BN_CLICKED(IDC_DELETE_SELECTED, OnDeleteSelected)
	ON_BN_CLICKED(IDC_FREEZE, OnFreeze)
	ON_NOTIFY(LVN_ITEMCHANGED, IDC_OBJECTS, OnItemchangedObjects)
	ON_BN_CLICKED(IDC_LIST_ENTITY, OnListType)
	ON_BN_CLICKED(IDC_LIST_PREFABS, OnListType)
	ON_BN_CLICKED(IDC_LIST_OTHER, OnListType)
	ON_BN_CLICKED(IDC_LIST_TAGPOINT, OnListType)
	ON_BN_CLICKED(IDC_LIST_GROUPS, OnListType)
	ON_BN_CLICKED(IDC_LIST_STATOBJ, OnListType)
	ON_BN_CLICKED(IDC_LIST_SOUND, OnListType)
	ON_BN_CLICKED(IDC_LIST_PATHS, OnListType)
	ON_BN_CLICKED(IDC_LIST_VOLUMES, OnListType)
	ON_BN_CLICKED(IDC_LIST_BRUSHES, OnListType)
	ON_BN_CLICKED(IDC_LIST_SOLIDS, OnListType)
	ON_BN_CLICKED(IDC_LIST_AIPOINTS, OnListType)
	ON_BN_CLICKED(IDC_LIST_BUILDING, OnListType)
	ON_BN_CLICKED(IDC_AUTOSELECT, OnListType)
	ON_BN_CLICKED(IDC_TREE, OnListType)
	ON_BN_CLICKED(IDC_SEARCHALLOBJECTS, OnListType )
	ON_BN_CLICKED(IDC_SELECT_PROPERTY_NAME, OnMatchPropertyName)
	ON_BN_CLICKED(IDC_SELECT_PROPERTY_VALUE, OnMatchPropertyValue)
	ON_BN_CLICKED(IDC_REFRESH,OnRefresh)
	ON_COMMAND(IDC_SELECT_LT_ALL, OnSelTypesAll)
	ON_COMMAND(IDC_SELECT_LT_NONE, OnSelTypesNone)
	ON_COMMAND(IDC_SELECT_LT_INVERT, OnSelTypesInvert)
	ON_NOTIFY(NM_DBLCLK, IDC_OBJECTS, OnDblclkObjects)
	ON_CBN_SELCHANGE( IDC_COMBO_LAST_COLUMN,OnSelectionChanged )
	ON_NOTIFY(LVN_GETDISPINFO, IDC_OBJECTS, GetDispInfo)
	ON_WM_TIMER()
END_MESSAGE_MAP()

int CSelectObjectDlg::ComparedEmptyStringAsLast(const CString &cmpText1, const CString &cmpText2, int &cmp, int &order)
{
	if ( (cmpText1.IsEmpty() || cmpText2.IsEmpty() )  )
	{
		if ( cmpText1.IsEmpty() && !cmpText2.IsEmpty() )
		{
			if (order==0 )
				cmp=1;
			else
				cmp=-1;
		}
		else if ( !cmpText1.IsEmpty() && cmpText2.IsEmpty() )
		{
			if (order==0 )
				cmp=-1;
			else
				cmp=1;
		}
	}

	return cmp;
}

bool CSelectObjectDlg::GetGeomCount(CBaseObject *pObject, int &val)
{
	val = -1;

	if (pObject)
	{
		CString sGeomFileStr = GetGeometryFile(pObject);
		if (!sGeomFileStr.IsEmpty())
		{
			val = stl::find_in_map(CSelectObjectDlg::m_mGeomCountMap,sGeomFileStr,0);
			return true;
		}
	}

	return false;
}

void CSelectObjectDlg::UpdateGeomCount()
{
	CSelectObjectDlg::m_mGeomCountMap.clear();

	std::vector<CBaseObject*> objects;
	m_pObjMan->GetObjects( objects );

	for (size_t i = 0; i < objects.size(); ++i)
	{
		CBaseObject *pObj = objects[i];

		if (pObj)
		{
			CString sGeomFileStr = GetGeometryFile(pObj);

			if (!sGeomFileStr.IsEmpty())
			{
				int nGeomCount = stl::find_in_map(CSelectObjectDlg::m_mGeomCountMap,sGeomFileStr,0);
				CSelectObjectDlg::m_mGeomCountMap[sGeomFileStr] = nGeomCount + 1;
			}
		}
	}
}

static int __stdcall CompareItems( LPARAM p1,LPARAM p2,LPARAM sort )
{
	CBaseObject *o1 = (CBaseObject*)p1;
	CBaseObject *o2 = (CBaseObject*)p2;
	int order = sort >> 8;
	int column = sort & 0xF;
	int cmp = 0;

	if (column == COLUMN_NAME)
	{
		 // Sort by name.
		cmp = stricmp( GetObjectName(o1), GetObjectName(o2) );
		cmp = CSelectObjectDlg::ComparedEmptyStringAsLast( GetObjectName(o1), GetObjectName(o2), cmp, order );
	}
	else if (column == COLUMN_SELECTED)
		cmp = (o1->IsSelected()?0:1) - (o2->IsSelected()?0:1);
	else if (column == COLUMN_TYPE)
	{
		// Sort by type.
		CString typeStr1 = o1->GetTypeDescription();
		CString typeStr2 = o2->GetTypeDescription();
		CSelectObjectDlg::GetExtraLightInfo(o1,typeStr1);
		CSelectObjectDlg::GetExtraLightInfo(o2,typeStr2);

		cmp = stricmp( typeStr1, typeStr2 );
		cmp = CSelectObjectDlg::ComparedEmptyStringAsLast( typeStr1, typeStr2, cmp, order );
	}
	else if (column == COLUMN_LAYER)
	{
		// Sort by layer
		cmp = stricmp( o1->GetLayer()->GetName(), o2->GetLayer()->GetName() );
		cmp = CSelectObjectDlg::ComparedEmptyStringAsLast(  o1->GetLayer()->GetName(), o2->GetLayer()->GetName(), cmp, order );
	}
	else if (column == COLUMN_DEFAULTMATERIAL)
	{
		CString matNameTxt1= GetMaterialName(o1);
		matNameTxt1.Replace(UNIX_SLASH,"");
		matNameTxt1.Replace(WIN_SLASH,"");
		CString matNameTxt2= GetMaterialName(o2);
		matNameTxt2.Replace(UNIX_SLASH,"");
		matNameTxt2.Replace(WIN_SLASH,"");

		cmp = stricmp( matNameTxt1 , matNameTxt2 );
		cmp = CSelectObjectDlg::ComparedEmptyStringAsLast( matNameTxt1, matNameTxt2, cmp, order );
	}
	else if (column == COLUMN_CUSTOMMATERIAL)
	{
		CString matNameTxt1= GetMaterialName(o1, TRUE);
		matNameTxt1.Replace(UNIX_SLASH,"");
		matNameTxt1.Replace(WIN_SLASH,"");
		CString matNameTxt2= GetMaterialName(o2, TRUE);
		matNameTxt2.Replace(UNIX_SLASH,"");
		matNameTxt2.Replace(WIN_SLASH,"");

		cmp = stricmp( matNameTxt1 , matNameTxt2 );
		cmp = CSelectObjectDlg::ComparedEmptyStringAsLast( matNameTxt1, matNameTxt2, cmp, order );
	}
	else if (column == COLUMN_SMART_OBJECT)
	{
		if(g_nCurSel<=0)
		{
			cmp = stricmp( GetSmartObject(o1), GetSmartObject(o2) );
			cmp = CSelectObjectDlg::ComparedEmptyStringAsLast( GetSmartObject(o1), GetSmartObject(o2), cmp, order );
		}
		else
		{
			int gropid1 = GetAIGroupID(o1);
			int gropid2 = GetAIGroupID(o2);
			if(gropid1==-1)
				gropid1 = MAXINT;
			if(gropid2==-1)
				gropid2 = MAXINT;
			cmp = gropid1 == gropid2 ? 0 : (gropid1>gropid2 ? 1 : -1);
		}
	}
	else if (column == COLUMN_GEOMETRY)
	{
		CString geomNameTxt1= GetGeometryFile(o1);
		geomNameTxt1.Replace(UNIX_SLASH,"");
		geomNameTxt1.Replace(WIN_SLASH,"");

		CString geomNameTxt2= GetGeometryFile(o2);
		geomNameTxt2.Replace(UNIX_SLASH,"");
		geomNameTxt2.Replace(WIN_SLASH,"");

		cmp = stricmp( geomNameTxt1 , geomNameTxt2 );
		cmp = CSelectObjectDlg::ComparedEmptyStringAsLast( geomNameTxt1, geomNameTxt2, cmp, order );
	}
	else if ( column == COLUMN_GEOMETRYCOUNT)
	{
		int nCount1 = -1;
		int nCount2 = -1;
		
		CSelectObjectDlg::GetGeomCount(o1,nCount1);
		CSelectObjectDlg::GetGeomCount(o2,nCount2);

		CString objStr1("");
		CString objStr2("");

		if(nCount1==-1)
		{
			nCount1 = MAXINT;
			objStr1="";
		}
		else
		{
			objStr1.Format("%d",nCount1);
		}


		if(nCount2==-1)
		{
			nCount2 = MAXINT;
			objStr2="";
		}
		else
		{
			objStr2.Format("%d",nCount2);
		}

		cmp = nCount1 == nCount2 ? 0 : (nCount1>nCount2 ? 1 : -1);
		cmp = CSelectObjectDlg::ComparedEmptyStringAsLast(  objStr1, objStr2, cmp, order );
	}
	else if ( column == COLUMN_GEOMETRYLOD)
	{
		int nLODNo1=-1;
		int nLODNo2=-1;
		CString sLODStr1("");
		CString sLODStr2("");

		if ( CSelectObjectDlg::GETLODNumber(o1,nLODNo1) )
		{
			sLODStr1.Format("%d",nLODNo1);
		}
		else
		{
			nLODNo1 = MAXINT;
		}

		if ( CSelectObjectDlg::GETLODNumber(o2,nLODNo2) )
		{
			sLODStr2.Format("%d",nLODNo2);
		}
		else
		{
			nLODNo2 = MAXINT;
		}

		cmp = nLODNo1 == nLODNo2 ? 0 : (nLODNo1>nLODNo2 ? 1 : -1);
		cmp = CSelectObjectDlg::ComparedEmptyStringAsLast(  sLODStr1, sLODStr2, cmp, order );
	}
	else if ( column == COLUMN_MINSPEC)
	{
		CString sSpecStr1("");
		CString sSpecStr2("");

		sSpecStr1 = CSelectObjectDlg::m_specMap[o1->GetMinSpec()];
		sSpecStr2 = CSelectObjectDlg::m_specMap[o2->GetMinSpec()];

		cmp = stricmp( sSpecStr1, sSpecStr2 );
		cmp = CSelectObjectDlg::ComparedEmptyStringAsLast(  sSpecStr1, sSpecStr2, cmp, order );
	}
	else if (column == COLUMN_TRACKVIEW)
	{
		cmp = stricmp( CSelectObjectDlg::GetTrackViewName(o1), CSelectObjectDlg::GetTrackViewName(o2) );
		cmp = CSelectObjectDlg::ComparedEmptyStringAsLast(  CSelectObjectDlg::GetTrackViewName(o1), CSelectObjectDlg::GetTrackViewName(o2), cmp, order );
	}
	else if (column == COLUMN_FLOWGRAPH)
	{
		cmp = stricmp( CSelectObjectDlg::GetFlowGraphNames(o1), CSelectObjectDlg::GetFlowGraphNames(o2) );
		cmp = CSelectObjectDlg::ComparedEmptyStringAsLast( CSelectObjectDlg::GetFlowGraphNames(o1), CSelectObjectDlg::GetFlowGraphNames(o2), cmp, order );
	}

	return order != 0 ? -cmp : cmp;
}

bool CSelectObjectDlg::GETLODNumber(CBaseObject *pObject, int &nLodNumber)
{
	IStatObj::SStatistics stats;
	nLodNumber=-1;

	if (pObject->IsKindOf(RUNTIME_CLASS(CBrushObject)))
	{
		CBrushObject *pBrush = (CBrushObject *)pObject;

		if (!pBrush)
		{
			return false;
		}

		IStatObj *pStatObj= pBrush->GetIStatObj();

		if (pStatObj)
		{
			pStatObj->GetStatistics( stats );
			nLodNumber=stats.nLods;
			return true;
		}
	}
	else if (pObject->IsKindOf(RUNTIME_CLASS(CGeomEntity)))
	{
		CGeomEntity *pGeomEntity = (CGeomEntity*)pObject;
		IRenderNode* pEngineNode = pGeomEntity->GetEngineNode();

		if (pEngineNode)
		{
			IStatObj *pEntityStatObj = pEngineNode->GetEntityStatObj();
			if (pEntityStatObj)
			{
				pEntityStatObj->GetStatistics( stats );
				nLodNumber=stats.nLods;
				return true;
			}
		}
	}
	else if(pObject->IsKindOf(RUNTIME_CLASS(CEntityObject)))
	{
		CEntityObject *pEntity = static_cast<CEntityObject*>(pObject);
		IRenderNode* pEngineNode = pEntity->GetEngineNode();

		if (pEngineNode)
		{
			IStatObj *pEntityStatObj = pEngineNode->GetEntityStatObj();

			if (pEntityStatObj)
			{
				pEntityStatObj->GetStatistics( stats );
				nLodNumber=stats.nLods;
				return true;
			}

			ICharacterInstance *iEntityCharacter = pEngineNode->GetEntityCharacter(0);

			if (iEntityCharacter)
			{
				ICharacterModel *pCharModel = iEntityCharacter->GetICharacterModel();

				if (pCharModel)
				{		
					if ( pCharModel->GetNumInstances() > 0 )
					{
						ICharacterInstance *pCharInstance = pCharModel->GetInstance(0);

						if (pCharInstance)
						{
							if ( ISkeletonPose *pSkeletonPose = pCharInstance->GetISkeletonPose() )
							{
								uint32 numJoints = pSkeletonPose->GetJointCount();

								// check StatObj attachments
								for(uint32 i=0; i<numJoints; ++i)
								{
									IStatObj *pStatObj = (IStatObj*)pSkeletonPose->GetStatObjOnJoint(i);

									if (pStatObj)
									{
										pStatObj->GetStatistics(stats);
										nLodNumber = stats.nLods;
										return true;
									}
								}
							}
						}
					}
				}
			}
		}
	}

	return false;
}

inline bool StlCompareItems( CBaseObject *o1,CBaseObject *o2 )
{
	return CompareItems( (LPARAM)o1,(LPARAM)o2,CSelectObjectDlg::m_sortFlags ) < 0;
}

/////////////////////////////////////////////////////////////////////////////
// CSelectObjectDlg message handlers

BOOL CSelectObjectDlg::OnInitDialog() 
{
	CXTResizeDialog::OnInitDialog();

	m_pObjMan = GetIEditor()->GetObjectManager();
	m_displayMode = DISPLAY_VISIBLE;
	m_picking = GetIEditor()->IsPicking();

	CEditTool *pTool = GetIEditor()->GetEditTool();
	if (pTool && pTool->IsKindOf(RUNTIME_CLASS(CLinkTool)))
		m_bIsLinkTool = true;

	if (GetIEditor()->IsPicking())
	{
		SetWindowText( "Pick Object" );
		m_list.ModifyStyle( 0,LVS_SINGLESEL );
	}

	if (m_bIsLinkTool)
	{
		SetWindowText( "Select Parent Object" );
		m_list.ModifyStyle( 0,LVS_SINGLESEL );
	}


	m_nameFilter = "";
	m_propertyFilter = "";
	m_bMatchPropertyName = true;

	CheckDlgButton( IDC_VISIBLE,BST_CHECKED );

	CRect rc;
	GetDlgItem(IDC_OBJECTS)->GetClientRect( rc );
	int w1 = 2*rc.right/4-20;
	int w2 = rc.right/3 + 10;
	int w3 = rc.right - w1 - w2 - 25;

	//m_imageList.Create(IDB_SELECT_OBJECT_LIST, 16, 1, RGB (255, 255, 255));
	CMFCUtils::LoadTrueColorImageList( m_imageList,IDB_SELECT_OBJECT_LIST,16,RGB(255,255,255) );

	m_comboLastColumn.AddString("Smart Object");
	m_comboLastColumn.AddString("AI GroupId");
	m_comboLastColumn.SetCurSel(0);

	LVCOLUMN lvc;
	ZeroStruct(lvc);

	m_list.SetExtendedStyle( LVS_EX_FLATSB|LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES|LVS_EX_HEADERDRAGDROP );
	m_list.InsertColumn( COLUMN_NAME,"Name",LVCFMT_LEFT,w1,COLUMN_NAME );
	m_list.InsertColumn( COLUMN_SELECTED,"Selected",LVCFMT_CENTER,20,COLUMN_SELECTED );
	m_list.InsertColumn( COLUMN_TYPE,"Type",LVCFMT_LEFT,60,COLUMN_TYPE );
	m_list.InsertColumn( COLUMN_LAYER,"Layer",LVCFMT_LEFT,w2,COLUMN_LAYER );
	m_list.InsertColumn( COLUMN_DEFAULTMATERIAL,"Default Material",LVCFMT_LEFT,80,COLUMN_DEFAULTMATERIAL );
	m_list.InsertColumn( COLUMN_CUSTOMMATERIAL,"Custom Material",LVCFMT_LEFT,80,COLUMN_CUSTOMMATERIAL );
	m_list.InsertColumn( COLUMN_SMART_OBJECT,"Smart Object",LVCFMT_LEFT,80,COLUMN_SMART_OBJECT );
	m_list.InsertColumn( COLUMN_TRACKVIEW,"TrackView",LVCFMT_LEFT,80,COLUMN_TRACKVIEW );
	m_list.InsertColumn( COLUMN_FLOWGRAPH,"FlowGraph",LVCFMT_LEFT,120,COLUMN_FLOWGRAPH );
	m_list.InsertColumn( COLUMN_GEOMETRY,"Geometry",LVCFMT_LEFT,250,COLUMN_GEOMETRY );
	m_list.InsertColumn( COLUMN_GEOMETRYCOUNT,"Instances In Level",LVCFMT_LEFT,80,COLUMN_GEOMETRYCOUNT );
	m_list.InsertColumn( COLUMN_GEOMETRYLOD,"Number Of LOD",LVCFMT_LEFT,80,COLUMN_GEOMETRYLOD );
	m_list.InsertColumn( COLUMN_MINSPEC,"Spec",LVCFMT_LEFT,50,COLUMN_MINSPEC );
	
	FillList(true);

	// Define resize anchors.
	SetResize( IDC_OBJECTS,SZ_RESIZE(1) );
	
	SetResize( IDOK,SZ_HORREPOS(1) );
	SetResize( IDC_SEL_NONE,SZ_HORREPOS(1) );
	SetResize( IDC_SEL_ALL,SZ_HORREPOS(1) );
	SetResize( IDC_SEL_INV,SZ_HORREPOS(1) );
	
	SetResize( IDC_STATIC,SZ_VERREPOS(1) );
	SetResize( IDC_STATIC1,SZ_HORREPOS(1) );
	SetResize( IDC_STATIC2,SZ_HORREPOS(1) );
	SetResize( IDC_NUM_OBJECTS,SZ_VERREPOS(1) );
	
	SetResize( IDC_FASTFIND,CXTResizeRect(0,1,1,1) );
	
	SetResize( IDC_LIST_ENTITY,SZ_HORREPOS(1) );
	SetResize( IDC_LIST_PREFABS,SZ_HORREPOS(1) );
	SetResize( IDC_LIST_AIPOINTS,SZ_HORREPOS(1) );
	SetResize( IDC_LIST_TAGPOINT,SZ_HORREPOS(1) );
	SetResize( IDC_LIST_OTHER,SZ_HORREPOS(1) );
	SetResize( IDC_LIST_GROUPS,SZ_HORREPOS(1) );
	SetResize( IDC_LIST_VOLUMES,SZ_HORREPOS(1) );
	SetResize( IDC_LIST_PATHS,SZ_HORREPOS(1) );
	SetResize( IDC_LIST_BRUSHES,SZ_HORREPOS(1) );
	SetResize( IDC_LIST_SOLIDS,SZ_HORREPOS(1) );

	SetResize( IDC_SELECT_LT_ALL,SZ_HORREPOS(1) );
	SetResize( IDC_SELECT_LT_NONE,SZ_HORREPOS(1) );
	SetResize( IDC_SELECT_LT_INVERT,SZ_HORREPOS(1) );

	SetResize( IDC_AUTOSELECT,SZ_HORREPOS(1) );
	SetResize( IDC_TREE,SZ_HORREPOS(1) );

	SetResize( IDC_VISIBLE,SZ_HORREPOS(1) );
	SetResize( IDC_HIDDEN,SZ_HORREPOS(1) );
	SetResize( IDC_FROZEN,SZ_HORREPOS(1) );
	
	SetResize( IDC_HIDE,SZ_HORREPOS(1) );
	SetResize( IDC_FREEZE,SZ_HORREPOS(1) );
	SetResize( IDC_DELETE_SELECTED,SZ_HORREPOS(1) );

	SetResize( IDC_PROPERTY_STATIC,SZ_HORREPOS(1) );
	SetResize( IDC_SELECT_PROPERTY_NAME,SZ_HORREPOS(1) );
	SetResize( IDC_SELECT_PROPERTY_VALUE,SZ_HORREPOS(1) );
	SetResize( IDC_PROPERTY,SZ_HORREPOS(1) );

	SetResize( IDC_REFRESH,SZ_REPOS(1) );

	SetResize( IDC_STATIC_LAST_COLUMN,SZ_HORREPOS(1) );
	SetResize( IDC_COMBO_LAST_COLUMN,SZ_HORREPOS(1) );
	SetResize( IDC_SEARCHALLOBJECTS,SZ_VERREPOS(1) );

	
	//SetResize( IDOK,SZ_REPOS(1) );
	//SetResize( IDCANCEL,SZ_REPOS(1) );

	//AutoLoadPlacement( "Dialogs\\SelectObject" );
	EnableControls();

	// Add event listener.
	GetIEditor()->GetObjectManager()->AddObjectEventListener( functor(*this,&CSelectObjectDlg::OnObjectEvent) );
	GetIEditor()->RegisterNotifyListener( this );
	GetIEditor()->GetObjectManager()->GetLayersManager()->AddUpdateListener( functor(*this,&CSelectObjectDlg::OnLayerUpdate) );
	GetIEditor()->GetFlowGraphManager()->AddListener(this);

	m_bTimerActivationRequest=FALSE;
	m_bTimerActivated=FALSE;

	return TRUE;  // return TRUE unless you set the focus to a control
	              // EXCEPTION: OCX Property Pages should return FALSE
}

//////////////////////////////////////////////////////////////////////////
void CSelectObjectDlg::OnDestroy()
{
	GetIEditor()->GetFlowGraphManager()->RemoveListener(this);
	GetIEditor()->GetObjectManager()->GetLayersManager()->RemoveUpdateListener( functor(*this,&CSelectObjectDlg::OnLayerUpdate) );
	GetIEditor()->UnregisterNotifyListener( this );
	// Remove event listener.
	GetIEditor()->GetObjectManager()->RemoveObjectEventListener( functor(*this,&CSelectObjectDlg::OnObjectEvent) );
	CXTResizeDialog::OnDestroy();

}

//////////////////////////////////////////////////////////////////////////
void CSelectObjectDlg::PostNcDestroy()
{
	CXTResizeDialog::PostNcDestroy();
	delete this;
	m_instance = 0;
}

//////////////////////////////////////////////////////////////////////////
void CSelectObjectDlg::OnEditorNotifyEvent( EEditorNotifyEvent event )
{
	switch (event)
	{
	case eNotify_OnBeginNewScene:
	case eNotify_OnCloseScene:
		m_list.SetRedraw(FALSE);
		m_list.DeleteAllItems();
		m_list.SetRedraw(TRUE);
		break;
	case eNotify_OnBeginSceneOpen:
		m_bIgnoreObjectCallback = true;
		break;
	case eNotify_OnEndSceneOpen:
		m_bIgnoreObjectCallback = false;
		FillList(true);
		break;

	case eNotify_OnIdleUpdate:
		if (m_bSelectionChanged)
		{
			UpdateStatusText();
			m_bSelectionChanged = false;
		}
		if (m_bLayerModified)
		{
			FillList();
			m_bLayerModified = false;
		}
		if(m_modifiedFlowGraphObjects.size())
		{
			for (size_t i = 0; i < m_modifiedFlowGraphObjects.size(); ++i)
			{
				UpdateFlowGraphInMaps(m_modifiedFlowGraphObjects[i]);
			}
			m_modifiedFlowGraphObjects.resize(0);
			m_list.Invalidate();
		}
		break;

	case eNotify_OnEditToolChange:
		{
			bool bNeedRefill = false;
			CEditTool *pTool = GetIEditor()->GetEditTool();

			if(m_bIsLinkTool || m_picking)
			{
				m_list.ModifyStyle( LVS_SINGLESEL, 0 );
				bNeedRefill = true;
				m_selectBtn.SetWindowText("Select");
			}

			m_bIsLinkTool = false;
			m_picking = GetIEditor()->IsPicking();
			
			if (pTool && pTool->IsKindOf(RUNTIME_CLASS(CLinkTool)))
			{
				m_bIsLinkTool = true;
				SetWindowText( "Select Parent Object" );
				m_list.ModifyStyle( 0,LVS_SINGLESEL );
				bNeedRefill = true;
				m_selectBtn.SetWindowText("Link");
			}
			
			if(m_picking)
			{
				SetWindowText( "Pick Object" );
				m_list.ModifyStyle( 0,LVS_SINGLESEL );
				bNeedRefill = true;
				m_selectBtn.SetWindowText("Pick");
			}

			if(bNeedRefill)
			{
				m_bIgnoreObjectCallback = true;
				m_list.SetRedraw(FALSE);
				m_list.DeleteAllItems();
				m_list.SetRedraw(TRUE);
				m_bIgnoreObjectCallback = false;
				FillList();
			}
		}
		break;

	case eNotify_OnUpdateTrackView:
		m_bTrackViewModified = true;
		UpdateStatusText();
		break;
	}
}

//////////////////////////////////////////////////////////////////////////
void CSelectObjectDlg::OnHyperGraphManagerEvent( EHyperGraphEvent event, IHyperGraph* pGraph, IHyperNode* pINode )
{
	if (!m_bIgnoreObjectCallback
			&& ( event==EHG_GRAPH_INVALIDATE || event==EHG_GRAPH_ADDED || event==EHG_GRAPH_REMOVED || event==EHG_GRAPH_UPDATE_ENTITY
				|| event==EHG_NODE_ADD || event==EHG_NODE_DELETE || event==EHG_NODE_UPDATE_ENTITY))
	{
		if (pGraph)
		{
			if ( ((CHyperGraph*)pGraph)->IsFlowGraph() )
			{
				CFlowGraph* pFlowGraph = (CFlowGraph*)pGraph;
				CEntityObject* pGraphObject = pFlowGraph->GetEntity();

				IHyperGraphEnumerator *pEnum = pFlowGraph->GetNodesEnumerator();
				for (IHyperNode *pINode = pEnum->GetFirst(); pINode; pINode = pEnum->GetNext())
				{
					if ( ((CHyperNode*)pINode)->IsFlowNode() && ((CHyperNode*)pINode)->GetFlowNodeId()!=InvalidFlowNodeId )
					{
						CFlowNode* pFlowNode = (CFlowNode*)pINode;
						CEntityObject* pNodeObject = pFlowNode->GetEntity();
						if(pNodeObject && pNodeObject!=pGraphObject)
						{
							m_modifiedFlowGraphObjects.push_back(pNodeObject->GetId());
						}
					}
				}
				if(pGraphObject)
					m_modifiedFlowGraphObjects.push_back(pGraphObject->GetId());
			}
		}
		else if (pINode)
		{
			CHyperGraph* pFlowGraph = (CHyperGraph*)pINode->GetGraph();
			if (pFlowGraph && pFlowGraph->IsFlowGraph()
					&& ((CHyperNode*)pINode)->IsFlowNode() && ((CHyperNode*)pINode)->GetFlowNodeId()!=InvalidFlowNodeId )
			{
				CFlowNode* pFlowNode = (CFlowNode*)pINode;
				if(pFlowNode->GetEntity())
				{
					m_modifiedFlowGraphObjects.push_back(pFlowNode->GetEntity()->GetId());
				}
			}
		}
	}
}


//////////////////////////////////////////////////////////////////////////
bool CSelectObjectDlg::AddObject( CBaseObject *obj,int level )
{
	if(m_bIsLinkTool && obj->IsSelected())
		return false;

	int objType = obj->GetType();
	if (gSettings.objectHideMask & objType)
		return false;

	if (obj->GetGroup())
	{
		if (!obj->GetGroup()->IsOpen() && !m_bSearchInsideObjects)
			return false;
	}

	if (m_displayMode == DISPLAY_VISIBLE)
	{
		if (obj->IsHidden()||obj->IsFrozen())
			return false;
	}
	else if (m_displayMode == DISPLAY_HIDDEN)
	{
		if (!obj->IsHidden())
			return false;
	}
	else if (m_displayMode == DISPLAY_FROZEN)
	{
		if (!obj->IsFrozen())
			return false;
	}

	CString typeName = obj->GetTypeDescription();

	if (!(objType & m_listMask))
		return false;

	CObjectLayer *layer = obj->GetLayer();
	if (layer->IsFrozen())
		return false;

	if (!layer->IsVisible())
		return false;

	/*
	if (type == OBJTYPE_ENTITY && obj->GetRuntimeClass()->IsDerivedFrom(RUNTIME_CLASS(CEntity)))
	{
	typeName = ((CEntity*)obj)->GetEntityClass();
	}
	*/
	CString objName = GetObjectName( obj );
	CString objTypeDescription = obj->GetTypeDescription();
	CString objLayerName = obj->GetLayer()->GetName();
	
	// Check if have name filter.
	if (!m_nameFilter.IsEmpty())
	{
		if ( (strstri(objName,m_nameFilter) == 0) && (strstri(objTypeDescription,m_nameFilter) == 0) && (strstri(objLayerName,m_nameFilter) == 0) )
		{
			return false;
		}
	}

	if (!m_propertyFilter.IsEmpty())
	{
		if (!IsPropertyMatch( obj ))
			return false;
	}

	if ( stl::find(m_objects,obj) )
		return false;

	m_objects.push_back(obj);
	m_indentState.push_back(level);

	return true;
}

//////////////////////////////////////////////////////////////////////////
void CSelectObjectDlg::AddObjectRecursively( CBaseObject *obj,int level )
{
	bool parentObjectAdded = AddObject( obj,level );
	std::vector<CBaseObject*> objects;
	for (int i = 0; i < obj->GetChildCount(); i++)
	{
		objects.push_back( obj->GetChild(i) );
	}

	std::sort( objects.begin(),objects.end(),StlCompareItems );

	for (int i = 0; i < objects.size(); i++)
	{
		int childLevel = ( parentObjectAdded ) ? level + 1 : level;
		AddObjectRecursively( objects[i], childLevel );
	}
}

//////////////////////////////////////////////////////////////////////////
void CSelectObjectDlg::FillList(bool bRebuildMaps)
{
	UpdateGeomCount();

	if (m_bTree)
		m_list.SetImageList( &m_imageList,LVSIL_SMALL );
	else
		m_list.SetImageList( NULL,LVSIL_SMALL );

	m_list.SetRedraw(FALSE);
	//m_list.LockWindowUpdate();
	m_list.DeleteAllItems();

	int numObjects = GetIEditor()->GetObjectManager()->GetObjectCount();
	std::vector<CBaseObject*> objects;
	objects.clear();
	objects.reserve( numObjects );
	GetIEditor()->GetObjectManager()->GetObjects( objects );
	m_objects.clear();
	m_indentState.clear();
	m_objects.reserve( numObjects );
	m_indentState.reserve( numObjects );

	if (bRebuildMaps)
		BuildMaps();

	int mask = 0;
	if (m_bEntity)
		mask |= OBJTYPE_ENTITY;
	if (m_bPrefabs)
		mask |= OBJTYPE_PREFAB;
	if (m_bTagPoint)
		mask |= OBJTYPE_TAGPOINT;
	if (m_bAIPoint)
		mask |= OBJTYPE_AIPOINT;
	if (m_bVolumes)
		mask |= OBJTYPE_VOLUME;
	if (m_bGroups)
		mask |= OBJTYPE_GROUP;
	if (m_bShapes)
		mask |= OBJTYPE_SHAPE;
	if (m_bBrushes)
		mask |= OBJTYPE_BRUSH;
	if (m_bSolids)
		mask |= OBJTYPE_SOLID;
	if (m_bOther)
		mask |= ~(OBJTYPE_ANY_DEFINED);
	m_listMask = mask;

	std::sort( objects.begin(),objects.end(),StlCompareItems );
	
	// Filter the object list properly.
	for (size_t i = 0; i < objects.size(); ++i)
	{
		CBaseObject *obj = objects[i];

		if (m_bTree && obj->GetParent() && !m_bSearchInsideObjects)
			continue;

		if (m_bTree || m_bSearchInsideObjects)
			AddObjectRecursively( obj,0 );
		else
			AddObject( obj,0 );
	}

	m_list.SetItemCountEx(m_objects.size(), LVSICF_NOSCROLL|LVSICF_NOINVALIDATEALL);

	if (bRebuildMaps)
		BuildMaps();
	
	//m_list.UnlockWindowUpdate();
	m_list.SetRedraw(TRUE);

	int nItemsCount = m_list.GetItemCount();

	if ( m_nLastSelectedItemPosition>0 && !m_list.Update(m_nLastSelectedItemPosition) )
		m_nLastSelectedItemPosition--;

	m_list.SetItemState(m_nLastSelectedItemPosition, LVIS_SELECTED, LVIS_SELECTED);
	m_list.EnsureVisible(m_nLastSelectedItemPosition,FALSE);

	UpdateStatusText();
}

//////////////////////////////////////////////////////////////////////////
void CSelectObjectDlg::UpdateStatusText()
{
	int numObjects = m_list.GetItemCount();
	int numSelected = m_list.GetSelectedCount();
	CString str;
	str.Format( "%d Objects Listed,  %d Selected",numObjects,numSelected );
	if (m_bTrackViewModified)
		str += " [TrackView column may contain stale data!]";
	m_numObjects.SetWindowText( str );

	m_bSelectionChanged = false;
}

//////////////////////////////////////////////////////////////////////////
afx_msg void CSelectObjectDlg::OnSelAll()
{
	m_bIgnoreObjectCallback = true;
	////////////////////////////////////////////////////////////////////////
	// Select all items in the list
	////////////////////////////////////////////////////////////////////////
	for (size_t i = 0; i < m_objects.size(); ++i)
	{
		m_list.SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
		SelectItemObject( i );
	}
	if (!m_picking && !m_bAutoselect)
		OnOK();
	m_list.Invalidate();
	m_list.SetFocus();
	m_bIgnoreObjectCallback = false;
}

afx_msg void CSelectObjectDlg::OnSelNone()
{
	m_bIgnoreObjectCallback = true;
	////////////////////////////////////////////////////////////////////////
	// Remove selection from all items in the list
	////////////////////////////////////////////////////////////////////////
	for (size_t i = 0; i < m_objects.size(); ++i)
	{
		m_list.SetItemState(i, 0, LVIS_SELECTED);
		SelectItemObject( i );
	}
	if (!m_picking && !m_bAutoselect)
		OnOK();

	m_list.Invalidate();
	m_list.SetFocus();
	m_bIgnoreObjectCallback = false;

	GetIEditor()->ClearSelection();
}

afx_msg void CSelectObjectDlg::OnSelInv()
{
	////////////////////////////////////////////////////////////////////////
	// Reverse selection of all items in the list
	////////////////////////////////////////////////////////////////////////
	m_bIgnoreObjectCallback = true;
	bool bChanged = false;
	for (size_t i = 0; i < m_objects.size(); ++i)
	{
		bool bSelected = m_list.GetItemState(i, LVIS_SELECTED) != 0;
		m_list.SetItemState(i, bSelected?0:LVIS_SELECTED, LVIS_SELECTED);
		SelectItemObject( i );
	}

	if (!m_picking && !m_bAutoselect)
		OnOK();

	m_bIgnoreObjectCallback = false;
	m_list.Invalidate();
	m_list.SetFocus();
}

void CSelectObjectDlg::OnOK() 
{
	//////////////////////////////////////////////////////////////////////
	// Write the changes to the selection back to the document
	//////////////////////////////////////////////////////////////////////
	/*
	if (!m_picking)
	{
		GetIEditor()->ClearSelection();
	}

	for (int i = 0; i < m_list.GetItemCount(); i++)
	{
		LVITEM item;
		ZeroStruct(item);
		item.mask = LVIF_STATE|LVIF_PARAM;
		item.stateMask = LVIS_SELECTED;
		item.iItem = i;
		m_list.GetItem( &item );
		if (item.state == LVIS_SELECTED)
		{
			CBaseObject *obj = (CBaseObject*)item.lParam;
			GetIEditor()->GetObjectManager()->SelectObject( obj );
			
			if (picking)
				break;
		}
	}
	*/
	m_bIgnoreObjectCallback = true;


	if (m_picking || !m_bAutoselect || m_bIsLinkTool)
	{
		if (!m_picking && !m_bIsLinkTool)
			GetIEditor()->ClearSelection();

		for (size_t i = 0; i < m_objects.size(); ++i)
		{
			if (m_list.GetItemState(i, LVIS_SELECTED) != 0)
			{
				CBaseObject *obj = m_objects[i];

				if(m_bIsLinkTool)
				{
					CEditTool *pTool = GetIEditor()->GetEditTool();
					if (pTool && pTool->IsKindOf(RUNTIME_CLASS(CLinkTool)))
					{
						((CLinkTool*)pTool)->LinkSelectedToParent(obj);
						GetIEditor()->SetEditTool(0);
						break;
					}
				}
				else
				{
					m_pObjMan->SelectObject( obj );
					if (m_picking)
						break;
				}
			}
		}
	}
	m_bIgnoreObjectCallback = false;
	m_bSelectionChanged = true;
	
	//DestroyWindow();
}

void CSelectObjectDlg::OnCancel() 
{
	//DestroyWindow();
}

void CSelectObjectDlg::OnListType() 
{
	UpdateData();
	FillList();
}

void CSelectObjectDlg::OnRefresh() 
{
	UpdateData();
	FillList(true);
}

void CSelectObjectDlg::OnColumnclickObjects(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
	// TODO: Add your control notification handler code here

	int order = 0;
	int column = pNMListView->iSubItem;
	int bit = 1 << column;
	if ((m_sortFlags&0xF) == column)
	{
		// change order.
	 order = m_sortFlags >> 8;
	 order = (order == 0) ? 1 : 0;
	}
	m_sortFlags = (order << 8) | column;
	// Resort items.
	if (!m_bTree)
		std::sort( m_objects.begin(), m_objects.end(),StlCompareItems );
	
	m_list.Invalidate();

	*pResult = 0;
}

void CSelectObjectDlg::OnTimer(UINT_PTR timerVal)
{
	if (!m_bTimerActivationRequest)
	{
		FillList();
		KillTimer(timerVal);
		m_bTimerActivated=FALSE;
	}

	m_bTimerActivationRequest=FALSE;
}

UINT CSelectObjectDlg::StartFilterTimer(UINT duration)
{
	UINT timerVal;
	timerVal = SetTimer(IDT_TIMER_0,duration,NULL);
	m_bTimerActivated=TRUE;
	return timerVal;
}

void CSelectObjectDlg::OnUpdateFastfind() 
{
	// TODO: If this is a RICHEDIT control, the control will not
	// send this notification unless you override the CXTResizeDialog::OnInitDialog()
	// function to send the EM_SETEVENTMASK message to the control
	// with the ENM_UPDATE flag ORed into the lParam mask.
	
	// TODO: Add your control notification handler code here
	CString findText;
	GetDlgItemText( IDC_FASTFIND,findText );
	//if (findText.IsEmpty())
		//return;
	
	m_nameFilter = findText;

	int objc = GetIEditor()->GetObjectManager()->GetObjectCount();

	if ( GetIEditor()->GetObjectManager()->GetObjectCount() < FILE_FILTER_DELAY_OBJ_COUNT )
	{
		FillList();
	}
	else
	{
		if (!m_bTimerActivated)
		{
			StartFilterTimer(FILE_FILTER_DELAY);
		}
		else
		{
			m_bTimerActivationRequest=TRUE;
		}
	}

	/*
	LVFINDINFO info;
	ZeroStruct(info);
	info.flags = LVFI_PARTIAL|LVFI_STRING;
	info.psz = findText;

	int nIndex=-1;
	POSITION pos = NULL;

	GetIEditor()->ClearSelection();
	// First unselect all.
	for (int i = 0; i < m_list.GetItemCount(); i++)
	{
		m_list.SetItem( i,0,LVIF_STATE,0,0,0,LVIS_SELECTED,0 );
	}

	// Find string partially from the begining.
	while ((nIndex = m_list.FindItem(&info,nIndex)) != -1)
	{
		m_list.SetItem( nIndex,0,LVIF_STATE,0,0,LVIS_SELECTED,LVIS_SELECTED,0 );
		SelectItemObject( i );
	}

	pos = m_list.GetFirstSelectedItemPosition();
	if (pos == NULL)
	{
		// Nothing selected.

		// Select all items that much this string.
		for (int i = 0; i < m_list.GetItemCount(); i++)
		{
			CString text = m_list.GetItemText( i,0 );
			if (strstr(text,findText) != 0)
			{
				m_list.SetItem( i,0,LVIF_STATE,0,0,LVIS_SELECTED,LVIS_SELECTED,0 );
				SelectItemObject( i );
			}
		}
	}

	// Ensure that first selected item is visible.
	pos = m_list.GetFirstSelectedItemPosition();
	if (pos != NULL)
	{
		int nItem = m_list.GetNextSelectedItem(pos);
		m_list.EnsureVisible(nItem,FALSE);
	}
	GetDlgItem(IDC_FASTFIND)->SetFocus();
	*/
}

void CSelectObjectDlg::OnClickObjects(NMHDR* pNMHDR, LRESULT* pResult) 
{
	/*
	CString text;
	GetDlgItemText( IDC_FASTFIND,text );
	if (!text.IsEmpty())
	{
		SetDlgItemText( IDC_FASTFIND,"" );
		m_nameFilter = "";
	}
	*/
	
	*pResult = 0;
}

void CSelectObjectDlg::EnableControls()
{
	BOOL bEnable;
	if (m_list.GetSelectedCount() > 0)
		bEnable = TRUE;
	else
		bEnable = FALSE;
		
	GetDlgItem(IDC_HIDE)->EnableWindow(bEnable);
	GetDlgItem(IDC_FREEZE)->EnableWindow(bEnable);
	GetDlgItem(IDC_DELETE_SELECTED)->EnableWindow(bEnable);
}

void CSelectObjectDlg::OnVisible() 
{
	m_displayMode = DISPLAY_VISIBLE;
	FillList();
	
	GetDlgItem(IDC_HIDE)->SetWindowText( "Hide" );
	GetDlgItem(IDC_FREEZE)->SetWindowText( "Freeze" );
	GetDlgItem(IDC_DELETE_SELECTED)->SetWindowText( "Delete Selected" );
}

void CSelectObjectDlg::OnHidden() 
{
	m_displayMode = DISPLAY_HIDDEN;
	FillList();

	GetDlgItem(IDC_HIDE)->SetWindowText( "Unhide" );
	GetDlgItem(IDC_FREEZE)->SetWindowText( "Freeze" );
	GetDlgItem(IDC_DELETE_SELECTED)->SetWindowText( "Delete Selected" );
}

void CSelectObjectDlg::OnFrozen() 
{
	m_displayMode = DISPLAY_FROZEN;
	FillList();
	GetDlgItem(IDC_HIDE)->SetWindowText( "Hide" );
	GetDlgItem(IDC_FREEZE)->SetWindowText( "Unfreeze" );
	GetDlgItem(IDC_DELETE_SELECTED)->SetWindowText( "Delete Selected" );
}

void CSelectObjectDlg::OnHide() 
{
	// Hide/Unhide selected objects.
	for (size_t i = 0; i < m_objects.size(); ++i)
	{
		if (m_list.GetItemState(i, LVIS_SELECTED) != 0)
		{
			CBaseObject *obj = m_objects[i];
			if (m_displayMode == DISPLAY_HIDDEN)
				GetIEditor()->GetObjectManager()->HideObject(obj,false);
			else
				GetIEditor()->GetObjectManager()->HideObject(obj,true);
		}
	}
	FillList();
}

void CSelectObjectDlg::OnFreeze() 
{
	// Freeze/Unfreeze selected objects.
	for (size_t i = 0; i < m_objects.size(); ++i)
	{
		if (m_list.GetItemState(i, LVIS_SELECTED) != 0)
		{
			CBaseObject *obj = m_objects[i];
			if (m_displayMode == DISPLAY_FROZEN)
				GetIEditor()->GetObjectManager()->FreezeObject(obj,false);
			else
				GetIEditor()->GetObjectManager()->FreezeObject(obj,true);
		}
	}
	FillList();
}

void CSelectObjectDlg::OnDeleteSelected()
{
  if (IDYES!=MessageBox("Are you sure you want to delete the selected objects?","Delete Objects",MB_YESNO))
	{
		return;
	}


	std::vector<CBaseObject*> cDeleteList;

	cDeleteList.reserve(m_objects.size());

	// Builds the list of objects to delete...
	for (size_t i = 0; i < m_objects.size(); ++i)
	{
		if (m_list.GetItemState(i, LVIS_SELECTED) != 0)
		{
			cDeleteList.push_back(m_objects[i]);
		}
	}

	// Deletes the objects.
	GetIEditor()->BeginUndo();
  size_t nTotal(cDeleteList.size());
	for (size_t nCount(0);nCount<nTotal;++nCount)
	{
		GetIEditor()->GetObjectManager()->DeleteObject(cDeleteList[nCount]);
	}
	GetIEditor()->AcceptUndo( "Delete Objects" );
	GetIEditor()->SetModifiedFlag();
	GetIEditor()->SetModifiedModule(eModifiedBrushes);

	
	FillList();
}

void CSelectObjectDlg::OnItemchangedObjects(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NMLISTVIEW* pNMListView = (NMLISTVIEW*)pNMHDR;
	if (pNMListView->iItem < 0 || pNMListView->iItem >= m_objects.size())
		return;
	CBaseObject *pObject = m_objects[pNMListView->iItem];
	if (pObject)
	{
		bool bWasSelected = (pNMListView->uOldState & LVIS_SELECTED) != 0;
		bool bNowSelected = (pNMListView->uNewState & LVIS_SELECTED) != 0;
		if (bWasSelected != bNowSelected)
		{
			SelectItemObject( pNMListView->iItem );
		}
	}

	EnableControls();

	POSITION pos = m_list.GetFirstSelectedItemPosition();
	m_nLastSelectedItemPosition = m_list.GetNextSelectedItem(pos);
	
	*pResult = 0;
}

void CSelectObjectDlg::OnDblclkObjects(NMHDR* pNMHDR, LRESULT* pResult) 
{
	if (m_picking || !m_bAutoselect)
	{
		OnOK();
		*pResult = 0;
		return;
	}

	// Select and Goto object.
	NMITEMACTIVATE *pNM = (NMITEMACTIVATE*)pNMHDR;
	if (pNM->iItem < 0 || pNM->iItem >= m_objects.size())
		return;
	CBaseObject *pObject = m_objects[pNM->iItem];
	if (pObject)
	{
		if (m_displayMode == DISPLAY_VISIBLE)
		{
			SelectItemObject( pNM->iItem );

			if (GetIEditor()->GetViewManager()->GetGameViewport())
				GetIEditor()->GetViewManager()->GetGameViewport()->CenterOnSelection();
		}
	}
	
	*pResult = 0;
}

//////////////////////////////////////////////////////////////////////////
void CSelectObjectDlg::OnObjectEvent( CBaseObject *pObject,int event )
{
	if (m_bIgnoreObjectCallback)
		return;

	switch (event)
	{
	case CBaseObject::ON_ADD:
		AddObject( pObject,0 );
		m_list.SetItemCountEx(m_objects.size(), LVSICF_NOSCROLL|LVSICF_NOINVALIDATEALL);
		m_bSelectionChanged = true;
		break;

	case CBaseObject::ON_DELETE:
	case CBaseObject::ON_SELECT:
	case CBaseObject::ON_UNSELECT:
	case CBaseObject::ON_RENAME:
		{
			m_bSelectionChanged = true;
			int iItem = -1;
			for(size_t i=0; i<m_objects.size(); ++i)
			{
				if(m_objects[i] == pObject)
				{
					iItem = i;
					break;
				}
			}
			if (iItem >= 0)
			{
				switch (event)
				{
				case CBaseObject::ON_DELETE:
					m_objects.erase(m_objects.begin()+iItem);
					m_indentState.erase(m_indentState.begin()+iItem);
					m_list.SetItemCountEx(m_objects.size(), LVSICF_NOSCROLL);
					break;
				case CBaseObject::ON_SELECT:
					m_list.SetItemState(iItem, LVIS_SELECTED, LVIS_SELECTED);
					m_list.EnsureVisible( iItem,FALSE);
					break;
				case CBaseObject::ON_UNSELECT:
					m_list.SetItemState(iItem, 0, LVIS_SELECTED);
					break;
				case CBaseObject::ON_RENAME:
					m_list.Update(iItem);
					break;
				}
			}
		}
		break;
	}
}

void CSelectObjectDlg::SelectItemObject( int iItem )
{
	if (!m_bAutoselect)
		return;

	if (m_picking || m_displayMode != DISPLAY_VISIBLE)
	{
		return;
	}
	m_bIgnoreObjectCallback = true;
	
	m_bSelectionChanged = true;

	CBaseObject *pObject = m_objects[iItem];
	if (pObject)
	{
		if (m_list.GetItemState(iItem, LVIS_SELECTED) != 0)
		{
			CGroup *pParentGroup = pObject->GetGroup();

			if (pParentGroup)
			{
				if ( !pParentGroup->IsOpen() )
					m_pObjMan->SelectObject(pParentGroup);
				else
					m_pObjMan->SelectObject(pObject);
			}
			else
				m_pObjMan->SelectObject(pObject);
		}
		else
		{
			m_pObjMan->UnselectObject(pObject);
			m_list.SetItemText( iItem,COLUMN_SELECTED,"" );
		}
	}
	m_bIgnoreObjectCallback = false;
}

//////////////////////////////////////////////////////////////////////////
void CSelectObjectDlg::OnMatchPropertyName()
{
	m_bMatchPropertyName = true;
	CWaitCursor wait;
	m_propertyFilterCtrl.GetWindowText( m_propertyFilter );
	FillList();
}

//////////////////////////////////////////////////////////////////////////
void CSelectObjectDlg::OnMatchPropertyValue()
{
	m_bMatchPropertyName = false;
	CWaitCursor wait;
	m_propertyFilterCtrl.GetWindowText( m_propertyFilter );
	FillList();
}

//////////////////////////////////////////////////////////////////////////
bool CSelectObjectDlg::IsPropertyMatch( CBaseObject *pObject )
{
	CVarBlock *pVars = pObject->GetVarBlock();
	if (pVars)
	{
		for (int i = 0; i < pVars->GetVarsCount(); i++)
		{
			IVariable *pVar = pVars->GetVariable(i);
			if (IsPropertyMatchVariable( pVar ))
				return true;
		}
	}

	if (pObject->IsKindOf( RUNTIME_CLASS(CEntityObject) ))
	{
		CEntityObject *pEntity = (CEntityObject*)pObject;
		{
			CVarBlock *pVars = pEntity->GetProperties();
			if (pVars)
			{
				for (int i = 0; i < pVars->GetVarsCount(); i++)
				{
					IVariable *pVar = pVars->GetVariable(i);
					if (IsPropertyMatchVariable( pVar ))
						return true;
				}
			}
		}
		{
			CVarBlock *pVars = pEntity->GetProperties2();
			if (pVars)
			{
				for (int i = 0; i < pVars->GetVarsCount(); i++)
				{
					IVariable *pVar = pVars->GetVariable(i);
					if (IsPropertyMatchVariable( pVar ))
						return true;
				}
			}
		}

		if (pEntity)
		{
			CString sPropFilter = m_propertyFilter.MakeLower();
			sPropFilter.Replace(UNIX_SLASH,WIN_SLASH);

			IRenderNode* pEngineNode = pEntity->GetEngineNode();

			if (pEngineNode)
			{
				CString sVarPath("");

				IStatObj *pEntityStatObj = pEngineNode->GetEntityStatObj();
				ICharacterInstance *iEntityCharacter = pEngineNode->GetEntityCharacter(0);

				if (pEntityStatObj)
					sVarPath = pEntityStatObj->GetFilePath();

				if (sVarPath.IsEmpty() && iEntityCharacter)
					sVarPath = iEntityCharacter->GetFilePath();

				sVarPath = sVarPath.MakeLower();
				sVarPath.Replace(UNIX_SLASH,WIN_SLASH);

				if (strstri(sVarPath,sPropFilter)!=0)
					return true;

				CVarBlock *pVars = pEntity->GetProperties();

				if ( sVarPath.IsEmpty() && pVars )
				{
					IVariable *pVar = pVars->FindVariable( sPropFilter );

					if (pVar)
						return true;
				}

			}
		}

	}

	return false;
}

//////////////////////////////////////////////////////////////////////////
bool CSelectObjectDlg::IsPropertyMatchVariable( IVariable *pVar )
{
	if (m_bMatchPropertyName)
	{
		if (strstri(pVar->GetHumanName(),m_propertyFilter) != 0)
			return true;
	}
	else
	{
		CString value;
		pVar->Get( value );
		if (strstri(value,m_propertyFilter) != 0)
			return true;
	}
	if (pVar->NumChildVars() > 0)
	{
		for (int i =  0; i < pVar->NumChildVars(); i++)
		{
			IVariable *pChildVar = pVar->GetChildVar( i );
			if (IsPropertyMatchVariable( pChildVar ))
				return true;
		}
	}
	return false;
}

//////////////////////////////////////////////////////////////////////////
void CSelectObjectDlg::OnSelectionChanged()
{
	LVCOLUMN Column;
	memset(&Column, 0, sizeof(Column));
	Column.mask = LVCF_TEXT;

	g_nCurSel = m_comboLastColumn.GetCurSel();
	if(g_nCurSel<=0)
		Column.pszText="Smart Object";
	else
		Column.pszText="AI GroupId";
	m_list.SetColumn(COLUMN_SMART_OBJECT, &Column);
	FillList();
}

//////////////////////////////////////////////////////////////////////////
void CSelectObjectDlg::UpdateListTypes(int nState)
{
	int buttons[]={IDC_LIST_ENTITY, IDC_LIST_BRUSHES, IDC_LIST_PREFABS, IDC_LIST_TAGPOINT, IDC_LIST_AIPOINTS, IDC_LIST_GROUPS, IDC_LIST_VOLUMES, IDC_LIST_PATHS, IDC_LIST_SOLIDS, IDC_LIST_OTHER};
	for(int i=0; i<sizeof(buttons)/sizeof(int); i++)
	{
		if(nState==0)      // ALL
			((CButton *)GetDlgItem(buttons[i]))->SetCheck(TRUE);
		else if(nState==1) // NONE
			((CButton *)GetDlgItem(buttons[i]))->SetCheck(FALSE);
		else if(nState==2) // INVERT
			((CButton *)GetDlgItem(buttons[i]))->SetCheck(!((CButton *)GetDlgItem(buttons[i]))->GetCheck());
	}

	UpdateData();
	FillList();
}

//////////////////////////////////////////////////////////////////////////
void CSelectObjectDlg::OnSelTypesAll()
{
	UpdateListTypes(0);
}

//////////////////////////////////////////////////////////////////////////
void CSelectObjectDlg::OnSelTypesNone()
{
	UpdateListTypes(1);
}

//////////////////////////////////////////////////////////////////////////
void CSelectObjectDlg::OnSelTypesInvert()
{
	UpdateListTypes(2);
}

//////////////////////////////////////////////////////////////////////////
void CSelectObjectDlg::OnLayerUpdate( int event,CObjectLayer *pLayer )
{
	switch (event)
	{
	case CObjectLayerManager::ON_LAYER_MODIFY:
		m_bLayerModified = true;
		break;
	}
}

//////////////////////////////////////////////////////////////////////////
void CSelectObjectDlg::AddObjectToMaps(CBaseObject* pObject)
{
	CString tv = ComputeTrackViewName(pObject);
	if (tv.IsEmpty() == false)
		s_trackViewMap[pObject] = tv;
	CString fg = ComputeFlowGraphNames(pObject);
	if (fg.IsEmpty() == false)
		s_flowGraphMap[pObject] = fg;
}


//////////////////////////////////////////////////////////////////////////
void CSelectObjectDlg::UpdateFlowGraphInMaps(REFGUID guid)
{
	CBaseObject* pObject = m_pObjMan->FindObject(guid);
	if(!pObject)
		return;
	s_flowGraphMap.erase(pObject);
	if(pObject->CheckFlags(OBJFLAG_DELETED))
		return;
	CString fg = ComputeFlowGraphNames(pObject);
	if (fg.IsEmpty() == false)
		s_flowGraphMap[pObject] = fg;
}


//////////////////////////////////////////////////////////////////////////
void CSelectObjectDlg::BuildMaps()
{
	s_flowGraphMap.clear();
	s_trackViewMap.clear();
	m_modifiedFlowGraphObjects.resize(0);

	for (size_t i=0; i<m_objects.size(); ++i)
	{
		AddObjectToMaps(m_objects[i]);
	}

	// Now it has the latest data again.
	m_bTrackViewModified = false;
}


//////////////////////////////////////////////////////////////////////////
void CSelectObjectDlg::GetDispInfo(NMHDR *pNMHDR, LRESULT *pResult)
{
	NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);

	//Create a pointer to the item
	LV_ITEM* pItem= &(pDispInfo)->item;

	//Which item number?
	int nItem = pItem->iItem;

	int numObjs = int(m_objects.size());
	if(nItem > numObjs-1)
		return; // Just to be safe

	CBaseObject *pObject = m_objects[nItem];
	int indentLevel = m_indentState[nItem];

	//Do we need text information?
	if(pItem->mask & LVIF_TEXT) 
	{
		CString csText = "";

		//Which column?
		if(pItem->iSubItem == COLUMN_NAME)
			csText = GetObjectName(pObject);
		else if (pItem->iSubItem == COLUMN_SELECTED)
			csText = pObject->IsSelected()?"x":"";
		else if (pItem->iSubItem == COLUMN_TYPE)
		{
			csText = pObject->GetTypeDescription();
			GetExtraLightInfo(pObject,csText);
		}
		else if (pItem->iSubItem == COLUMN_LAYER)
			csText = pObject->GetLayer()->GetName();
		else if(pItem->iSubItem == COLUMN_DEFAULTMATERIAL)
			csText = GetMaterialName(pObject);
		else if(pItem->iSubItem == COLUMN_CUSTOMMATERIAL)
			csText = GetMaterialName(pObject, TRUE);
		else if (pItem->iSubItem == COLUMN_GEOMETRY)
		{
			csText=GetGeometryFile(pObject);
		}
		else if (pItem->iSubItem == COLUMN_GEOMETRYCOUNT)
		{
			int nVal=-1;
			CSelectObjectDlg::GetGeomCount(pObject,nVal);

			if (nVal==-1)
			{
				csText="";
			}
			else
			{
				csText.Format("%d",nVal);
			}
		}
		else if (pItem->iSubItem == COLUMN_GEOMETRYLOD)
		{
			int nLODNo1=-1;
			if ( CSelectObjectDlg::GETLODNumber(pObject,nLODNo1) )
			{
				csText.Format("%d",nLODNo1);
			}
			else
			{
				csText="";
			}
		}
		else if (pItem->iSubItem == COLUMN_MINSPEC)
		{	
			csText = m_specMap[pObject->GetMinSpec()];
		}
		else if (pItem->iSubItem == COLUMN_SMART_OBJECT)
		{
			if(g_nCurSel<=0)
			{
				csText = GetSmartObject(pObject);
			}
			else
			{
				int groupid = GetAIGroupID(pObject);
				if(groupid !=-1)
				{
					csText.Format("%d", groupid);
				}
			}
		}
		else if (pItem->iSubItem == COLUMN_TRACKVIEW)
			csText = GetTrackViewName(pObject);
		else if (pItem->iSubItem == COLUMN_FLOWGRAPH)
			csText = GetFlowGraphNames(pObject);

		//Copy the text to the LV_ITEM structure
		//Maximum number of characters is in pItem->cchTextMax
		lstrcpyn(pItem->pszText, csText, pItem->cchTextMax);
	}

	//Does the list need image information?
	if(pItem->mask & LVIF_IMAGE) 
	{
		int iImage = LIST_BITMAP_ANY;
		switch (pObject->GetType())
		{
		case OBJTYPE_GROUP:
			iImage = LIST_BITMAP_GROUP;
			break;
		case OBJTYPE_TAGPOINT:
			iImage = LIST_BITMAP_TAGPOINT;
			break;
		case OBJTYPE_ENTITY:
			iImage = LIST_BITMAP_ENTITY;
			break;
		case OBJTYPE_SHAPE:
			iImage = LIST_BITMAP_PATH;
			break;
		case OBJTYPE_VOLUME:
			iImage = LIST_BITMAP_VOLUME;
			break;
		case OBJTYPE_BRUSH:
			iImage = LIST_BITMAP_BRUSH;
			break;
		}
		pItem->iImage = iImage;
	}

	if(pItem->mask & LVIF_INDENT) 
	{
		pItem->iIndent = indentLevel;
	}

	*pResult = 0;
}


//////////////////////////////////////////////////////////////////////////
void CSelectObjectDlg::GetExtraLightInfo(CBaseObject *pObject, CString &csText)
{
	if (pObject->IsKindOf(RUNTIME_CLASS(CEntityObject)))
	{
		CEntityObject *pEntity = (CEntityObject*)pObject;
		if (pEntity)				
		{
			IObjectManager *objMan = GetIEditor()->GetObjectManager();

			if (objMan)
			{
				if ( objMan->IsLightClass(pEntity) )
				{
					if (!pEntity->GetEntityPropertyString("file_deferred_clip_geom").IsEmpty())
						csText += ".clip";
					
					if (pEntity->GetEntityPropertyBool("bAmbientLight"))
						csText += ".ambient";

					if (!pEntity->GetEntityPropertyString("texture_Texture").IsEmpty())
						csText += ".projector";

					if (!pEntity->GetEntityPropertyString("texture_deferred_cubemap").IsEmpty())
						csText += ".cubemap";

					int nLightType = pEntity->GetEntityPropertyInteger("nCastShadows");

					if (nLightType>0)
					{
						csText += ".shadow";

						switch (nLightType)
						{
						case 1:
							csText+=".lowSpec";
							break;
						case 2:
							csText+=".console/mediumSpec";
							break;
						case 3:
							csText+=".highSpec";
							break;
						case 4:
							csText+=".veryHighSpec";
							break;
						case 7:
							csText+=".detailSpec";
							break;
						}
					}
				}
			}
		}	
	}
}
